class MyVue{
    constructor(options){
        console.log(1,options)
        //$data 是要绑定在vm实例上
        this.$data = options.data
        Observber(this.$data)
        //代理劫持： name len other属性 绑定到 vm
        Object.keys(this.$data).forEach(key=>{
            //vm 是 MyVue类的实例  === this
            Object.defineProperty(this,key,{
                enumerable:true,
                configurable:true,
                get(){
                    return this.$data[key]
                },
                set(newVal){
                    this.$data[key] = newVal
                }
            })
        })
        // data数据放到页面上，对html进行编译
        Compile(options.el,this)

    }
}
//数据劫持
function Observber(obj){
    // console.log(3,obj)
    //为所有的属性 添加get 和 set 
    //终止条件：
    if(!obj || typeof obj!=="object")
    return ;
    var dep = new Dep();
    Object.keys(obj).forEach(key=>{
        // console.log(4,obj,key)
        let value = obj[key]
        //下一层对象 拦截
        Observber(value)
        Object.defineProperty(obj,key,{
            enumerable:true,
            configurable:true,
            get(){
                // console.log("key的值:"+value)
                //向Dep 存值
                Dep.target && dep.addSub(Dep.target)
                return value
            },
            set(newVal){
                // console.log('赋值')
                value = newVal
                Observber(value)//为下一层添加get和set
                dep.notify() //通知所有订阅者，有更新，应该更新watcher 的DOM结构
            }
        })
    })
}
//模板编译：
function Compile(el,vm){
    vm.$el = document.getElementById(el)
    console.log(6,vm.$el)
    //页面上直接进行数据替换 ---》 异步
    //文档碎片 在缓存中进行
    //创建文档随便
    const fragment = document.createDocumentFragment();
    //每个子节点 放到fragment
    while(childNode = vm.$el.firstChild){
        fragment.appendChild(childNode)  // 页面中的节点都放到fragment
    }
    // 模板编译
     replace(fragment)
    //把碎片中的内容 放大文档中
    vm.$el.appendChild(fragment)
     
    function replace(node){
        // {{  name   }} --> value
        const regMustache = /\{\{\s*(\S+)\s*\}\}/
        if(node.nodeType===3){ //文本节点
            // console.log(7,node.textContent)
            // 从文本节点 匹配和 替换
            const text = node.textContent
            const execRes = regMustache.exec(text)
            // console.log(8,execRes)
            if(execRes){
                const value = execRes[1].split('.').reduce((newObj,k)=>newObj[k],vm)
                // console.log(9,value)
                node.textContent = text.replace(regMustache,value)
                //替换：//通知所有订阅
           
                new Watcher(vm,execRes[1],(newval)=>{
                    node.textContent = text.replace(regMustache,newval)
                })
            }
            return
        }
        //实现双向绑定：
        //判断是否dom
        if(node.nodeType===1 && node.tagName.toUpperCase()=="INPUT"){
            const attrs = Array.from(node.attributes)
            console.log(9,attrs)
            //找到v-model的属性：
            const findRes = attrs.find(x=>x.name==='v-model')
            console.log(10,findRes)
            if(findRes){
                //获取v-model中的值 
                const expStr = findRes.value
                //获取 other.weight
                const value = expStr.split('.').reduce((newObj,k)=>newObj[k],vm)
                console.log(11,value)
                node.value = value
                //告诉订阅者 修改值
                new Watcher(vm,expStr,(newVal)=>{
                    node.value = newVal
                })
                //监听input的输入事件
                node.addEventListener('input',(e)=>{
                    const keyArr = expStr.split('.') 
                    console.log(12,keyArr) 
                    const obj = keyArr.slice(0,keyArr.length-1).reduce((newObj,k)=>newObj[k],vm)
                    obj[keyArr[keyArr.length-1]] = e.target.value
                    
                })
            }
        }
        node.childNodes.forEach((child)=>{
            replace(child)
        })
    }
}
//发布订阅模式：
class Dep{  //以来收集
    constructor(){
        this.subs = []
    }
    addSub(watcher){
        this.subs.push(watcher)
    }
    //set的时候 通知每一个watcher，去更新自己的DOM结构
    notify(){
        this.subs.forEach(watcher=>watcher.update())
    }
}

//订阅者
class Watcher{
    //cb 回调，记录当前watcher如何更新自己文本内容
    // key 属性名称
    // vm 实例对象， 包含更新数据
    // 从vm中去到key 对应的值，更新自己
    constructor(vm,key,cb){
        this.cb = cb
        this.key = key
        this.vm = vm
        //以下代码 是把watcher 存储到 Dep 的subs中
        Dep.target = this  //Dep.target 指向watcher
        //遍历vm上取到的值，去到vm中值，触发get内容
        key.split(".").reduce((newObj,k)=>newObj[k],vm)
        Dep.target = null
    }
    update(){ //给发布者用的，通知watcher 更新数据了
        const value = this.key.split('.').reduce((newObj,k)=>newObj[k],this.vm)
        this.cb(value)
    }
}